package zcj.stu.C49;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.ResolvableType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

@Configuration
@Slf4j
public class C49 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(C49.class);
//        SmsApplicationListener sms = context.getBean(SmsApplicationListener.class);
        MyService myService = context.getBean(MyService.class);
        myService.doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyService {
        @Autowired
        private ApplicationEventPublisher publisher;

        public void doBusiness() {
            System.out.println("主线任务...");
            publisher.publishEvent(new MyEvent("s_doBusiness"));
        }
    }

    @Component
    static class SmsApplicationListener {
        @MyListener
        public void sendMoney(MyEvent event) {
            System.out.println(event);
//            System.out.println();
            log.debug("给用户回馈了0.01元...");
        }

        public void charge() {
            System.out.println("充值...");
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    static @interface MyListener {

    }

    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            System.out.println(event);
            log.debug("发生了一条邮件...");
        }

        @MyListener
        public void charge(MyEvent event) {
            System.out.println("邮箱充值...");
        }

        @MyListener
        public void charge1(MyEvent event) {
            System.out.println("邮箱充值1...");
        }
    }

    @Bean
    static SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
        return () -> {
            for (String beanName : context.getBeanDefinitionNames()) {
                Object bean = context.getBean(beanName);
                for (Method method : bean.getClass().getMethods()) {
                    if (method.isAnnotationPresent(MyListener.class)) {
                        ApplicationListener listener = new ApplicationListener() {
                            @Override
                            public void onApplicationEvent(ApplicationEvent event) {
//                        if(event.getClass()!=method.getParameterTypes()[0])return;
                                if (!method.getParameterTypes()[0].isAssignableFrom(event.getClass())) return;
                                try {
                                    method.invoke(bean, event);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        };
                        context.addApplicationListener(listener);
                    }
                }
            }
        };
    }

    @Bean
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        return executor;
    }

    abstract static class MyApplicationEventMulticaster implements ApplicationEventMulticaster {
        @Override
        public void addApplicationListener(ApplicationListener<?> listener) {

        }

        @Override
        public void addApplicationListenerBean(String listenerBeanName) {

        }

        @Override
        public void removeApplicationListener(ApplicationListener<?> listener) {

        }

        @Override
        public void removeApplicationListenerBean(String listenerBeanName) {

        }

        @Override
        public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {

        }

        @Override
        public void removeApplicationListenerBeans(Predicate<String> predicate) {

        }

        @Override
        public void removeAllListeners() {

        }

        @Override
        public void multicastEvent(ApplicationEvent event) {

        }

        @Override
        public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {

        }
    }

    @Bean
    ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context, ThreadPoolTaskExecutor executor) {
        return new MyApplicationEventMulticaster() {
            List<GenericApplicationListener> listeners = new ArrayList<>();

            @Override
            public void addApplicationListenerBean(String listenerBeanName) {
//                System.out.println("??????????????????"+listenerBeanName);
                ApplicationListener listener = (ApplicationListener) context.getBean(listenerBeanName);
                ResolvableType genericType = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();
                GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
                    @Override
                    public void onApplicationEvent(ApplicationEvent event) {
                        listener.onApplicationEvent(event);
                    }

                    @Override
                    public boolean supportsEventType(ResolvableType eventType) {
                        return genericType.isAssignableFrom(eventType);
                    }
                };
                listeners.add(genericApplicationListener);
            }

            @Override
            public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
//                for (ApplicationListener listener : listeners) {
//                    ResolvableType generic = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();
//                    if(generic.isAssignableFrom(event.getClass())){
//                        listener.onApplicationEvent(event);
//                    }
//                }
                for (GenericApplicationListener listener : listeners) {
                    if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                        executor.submit(() -> listener.onApplicationEvent(event));
                    }
                }
            }
        };
    }
//    @Component
//    static class Feed{
//        @EventListener
//        public void sendMoney(){
//            System.out.println("给用户回馈了0.01元...");
//        }
//    }
}
